# package deb

# import (
# 	"fmt"
# 	"path/filepath"
# 	"regexp"
# 	"strings"
# )
from deb.listp import *
from utils.patch import *
from deb.version import *
import re
# // PackageLike is something like Package :) To be refined later
class PackageLike :
	
	def GetField(s,stra) ->str:
		print('父类方法')
	def MatchesDependency(s,Dependency)-> bool:
		print('父类方法')
	def MatchesArchitecture(s,stra) ->bool:
		print('父类方法')
	def GetName(s) ->str:
		print('父类方法')
	def GetVersion(s) ->str:
		print('父类方法')
	def GetArchitecture(s) ->str:
		print('父类方法')

# // PackageCatalog is abstraction on top of PackageCollection and PackageList
class PackageCatalog :
	
	def Scan(s,q ) ->PackageList:
		print('父类方法')
	def Search(s,dep :Dependency, allMatches :bool) ->List [Package]:
		print('父类方法')
	def SearchSupported(s,) ->bool:
		print('父类方法')
	def SearchByKey(s,arch, name, version :str)->PackageList:
		print('父类方法')



# // PackageQuery is interface of predicate on Package
class PackageQuery :
	
# 	// Matches calculates match of condition against package
	def Matches(s,pkg :PackageLike) ->bool:
		print('父类方法')
# 	// Fast returns if search strategy is possible for this query
	def Fast(s,packageCatalogList :PackageCatalog) ->bool:
		print('父类方法')
# 	// Query performs search on package list
	def Query(s,packageCatalogList :PackageCatalog) ->PackageList:
		print('父类方法')
# 	// String interface
	def String(s) ->str:
		print('父类方法')
# }

# // OrQuery is L | R
class OrQuery:
	L:PackageQuery=None
	R :PackageQuery=None
	# // Matches if any of L, R matches
	def  Matches(q,pkg :PackageLike) ->bool :
		return q.L.Matches(pkg) or q.R.Matches(pkg)
	

	# // Fast is True only if both parts are fast
	def   Fast(q,packageCatalogList :PackageCatalog) ->bool :
		return q.L.Fast(packageCatalogList) and q.R.Fast(packageCatalogList)
	

	# // Query strategy depends on nodes
	def  Query(q,packageCatalogList :PackageCatalog) ->PackageList :
		if q.Fast(packageCatalogList) :
			result = q.L.Query(packageCatalogList)
			result.Append(q.R.Query(packageCatalogList))
		else :
			result = packageCatalogList.Scan(q)
		
		return result
	

	# // String interface
	def  String(q) ->str :
		return "(%s) | (%s)".format( q.L, q.R)
	


# // AndQuery is L , R
class AndQuery :
	L:PackageQuery=None
	R :PackageQuery=None

	# // Matches if both of L, R matches
	def  Matches(q,pkg :PackageLike) ->bool :
		return q.L.Matches(pkg) and q.R.Matches(pkg)
	

	# // Fast is True if any of the parts are fast
	def   Fast(q,packageCatalogList :PackageCatalog) ->bool :
		return q.L.Fast(list) or q.R.Fast(packageCatalogList)
	

	# // Query strategy depends on nodes
	def  Query(q,packageCatalogList :PackageCatalog) ->PackageList :
		if not q.Fast(packageCatalogList) :
			result = packageCatalogList.Scan(q)
		else :
			if q.L.Fast(packageCatalogList) :
				result = q.L.Query(packageCatalogList)
				result = result.Scan(q.R)
			else :
				result = q.R.Query(packageCatalogList)
				result = result.Scan(q.L)
			
		
		return result

	# // String interface
	def  String(q) ->str :
		return "({}), ({})".format(q.L, q.R)
	


# // NotQuery is ! Q
class NotQuery :
	Q :PackageQuery=None

	# // Matches if not matches
	def  Matches(q,pkg :PackageLike) ->bool :
		return not q.Q.Matches(pkg)
	

	# // Fast is False
	def   Fast(q,packageCatalogList :PackageCatalog) ->bool :
		return False
	

	# // Query strategy is scan always
	def  Query(q,packageCatalogList :PackageCatalog) ->PackageList :
		result = packageCatalogList.Scan(q)
		return result
	

	# // String interface
	def  String(q) ->str :
		return "!({})".format(q.Q)
	


# // FieldQuery is generic request against field
class FieldQuery :
	Field    :str=''
	Relation :int=0
	Value    :str=''
	Regexp   =None #*regexp.Regexp `codec:"-"`

	# // Matches on generic field
	def  Matches(q,pkg :PackageLike) ->bool :
		if q.Field == "$Version" :
			dependency=Dependency ()
			dependency.Pkg          =pkg.GetName()
			dependency.Relation     =q.Relation
			dependency.Version     =q.Value
			dependency.Regexp       =q.Regexp
			return pkg.MatchesDependency(dependency)
		
		if q.Field == "$Architecture" and q.Relation == VersionRelation. VersionEqual :
			return pkg.MatchesArchitecture(q.Value)
		

		field = pkg.GetField(q.Field)

		
		if q.Relation == VersionRelation. VersionDontCare:
			return field != ""
		elif q.Relation == VersionRelation. VersionEqual:
			return CompareVersions(field ,q.Value)==0
		elif q.Relation == VersionRelation. VersionGreater:
			return CompareVersions(field ,q.Value)>0
		elif q.Relation == VersionRelation. VersionGreaterOrEqual:
			return CompareVersions(field ,q.Value)>=0
		elif q.Relation == VersionRelation. VersionLess:
			return CompareVersions(field ,q.Value)<0
		elif q.Relation == VersionRelation. VersionLessOrEqual:
			return CompareVersions(field ,q.Value)<=0
		elif q.Relation == VersionRelation. VersionPatternMatch:
			matched = Match(q.Value, field)
			return  matched
		elif q.Relation == VersionRelation. VersionRegexp:
			if q.Regexp is None  :
				q.Regexp = re.compile(q.Value)
			
			return q.Regexp.search(field).span() is not None 

		
		raise Exception("unknown relation")
	

	# // Query runs iteration through list
	def  Query(q,packageCatalogList :PackageCatalog) ->PackageList :
		result = packageCatalogList.Scan(q)
		return result
	

	# // Fast depends on the query
	def   Fast(q,packageCatalogList :PackageCatalog) ->bool :
		return False
	

	# // String interface
	def  String(q) ->str :
		def escape(val :str) ->str :
			if containsAny(val, "()|,!{} \t\n") :
				return "'" + val.replace( "\\", "\\\\").replace( "'", "\\'") + "'"
			
			return val
		

		
		op=None
		if q.Relation == VersionRelation.VersionEqual:
			op = "="
		elif q.Relation == VersionRelation.VersionGreater:
			op = ">>"
		elif q.Relation == VersionRelation.VersionLess:
			op = "<<"
		elif q.Relation == VersionRelation.VersionRegexp:
			op = "~"
		elif q.Relation == VersionRelation.VersionPatternMatch:
			op = "%"
		elif q.Relation == VersionRelation.VersionGreaterOrEqual:
			op = ">="
		elif q.Relation == VersionRelation.VersionLessOrEqual:
			op = "<="
		
		return "{} ({} {})".format( escape(q.Field), op, escape(q.Value))


from deb.version import *
# // PkgQuery is search request against specific package
class PkgQuery :
	Pkg:str=''
	Version :str=''
	Arch    :str=''

	# // Matches on specific properties
	def  Matches(q,pkg :PackageLike) ->bool :
		return pkg.GetName() == q.Pkg and pkg.GetVersion() == q.Version and pkg.GetArchitecture() == q.Arch
	

	# // Fast is always True for package query
	def   Fast(q,packageCatalogList :PackageCatalog) ->bool :
		return True
	

	# // Query looks up specific package
	def  Query(q,packageCatalogList :PackageCatalog) ->PackageList :
		return packageCatalogList.SearchByKey(q.Arch, q.Pkg, q.Version)
	

	# // String interface
	def  String(q) ->str :
		return "%s_%s_%s".format(q.Pkg, q.Version, q.Arch)
	



# // DependencyQuery is generic Debian-dependency like query
class DependencyQuery :
	Dep :Dependency=None

	# // Matches on dependency condition
	def  Matches(q,pkg :PackageLike) ->bool :
		return pkg.MatchesDependency(q.Dep)
	

	# // Fast is always True for dependency query
	def   Fast(q,packageCatalogList :PackageCatalog) ->bool :
		return packageCatalogList.SearchSupported()
	

	# // Query runs PackageList.Search，*deb.PackageList
	def  Query(q,packageCatalogList :PackageCatalog) ->PackageList :
		if q.Fast(packageCatalogList) :
			result = NewPackageList()
			for   pkg in packageCatalogList.Search(q.Dep, True) :
				result.Add(pkg)
			
		else :
			result = packageCatalogList.Scan(q)
		

		return
	

	# // String interface
	def  String(q) ->str :
		return q.Dep.String()
	


# // MatchAllQuery is query that matches all the packages
class MatchAllQuery :

	# // Matches on specific properties
	def  Matches(q,pkg :PackageLike) ->bool :
		return True
	# }

	# // Fast is always True for match all query
	def   Fast(q,packageCatalogList :PackageCatalog) ->bool :
		return True
	# }

	# // Query looks up specific package
	def  Query(q,packageCatalogList :PackageCatalog) ->PackageList :
		return list.Scan(q)
	# }

	# // String interface
	def  String(q) ->str :
		return ""
	# }
